This tutorial will demonstrate how to analyse audio data for acoustic phonetic studies in R. It is mainly intended to demonstrated possible workflows. The topics covered are:
- phonetic databases, the case of
emuR and EMU-SDMS, the EMU Speech Database Management System
- Sample data: the LOD database
- Queries and requeries
- Inscpect the database:
serve()
- Calculate duration for vowel categories
- Vowels formants and visualisations with
ggplot2
- Vowel explorer
- Calculating the Pillai distance
Preamble
The generated HTML page of this tutorial is available here.
This tutorial is organised as an R Markdown notebook. To execute the code within this notebook (filename Lecture.Rmd) it has to be opend in RStudio. When executing code, the results appear beneath the code. In order to do so, you need to have R and RStudio installed. When this R notebook is loaded into RStudio, you can excecute chunks by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter, allowing you to experiment with the code. This handout contains all output of the code (tables, visualisations etc.). The easiest way to work with this R code is to clone the entire project from this GitHub repository.
The following libraries will be needed and have to be installed.
library(tidyverse)
library(ggiraph)
library(cowplot)
library(emuR)
library(tools)
library(rio)
library(ggplot2)
library(magrittr)
library(ggiraph)
library(htmltools)
library(shiny)
library(joeyr)
library(knitr)
The LOD database
The database contains the audio recordings from the Lëtzebuerger Online Dictionnaire (available here, spoken by one female speaker. The audio files have been automatically segmented with the MAUS tools. We thus have a database conisting of textual data, basically words, and the corresponding audio data. The audio data is segmented into words and phonetic segments (sounds).
This database has been created beforehand. Infos how to create such a database is explained in the EMU-SDMS manual.
While we will be working with the full database (26.000 recordings), a small demo database lod_emuDB has been created with 500 recordings which can be used for individual testing.
We start with loading the database and give an overview of structure and content.
# load database
db = load_emuDB("/Users/peter.gilles/Documents/_Daten/LOD-emuDB/lod_emuDB")
INFO: Checking if cache needs update for 1 sessions and 26093 bundles ...
INFO: Performing precheck and calculating checksums (== MD5 sums) for _annot.json files ...
INFO: Nothing to update!
#db = load_emuDB("lod_emuDB")
# display the overview of the structure and content
summary(db)
Name: lod
UUID: c87793c0-012a-11e9-874b-68b599b5deb4
Directory: /Users/peter.gilles/Documents/_Daten/LOD-emuDB/lod_emuDB
Session count: 1
Bundle count: 26093
Annotation item count: 515638
Label count: 639542
Link count: 489545
Database configuration:
SSFF track definitions:
Level definitions:
Link definitions:
Tracks in emuR are acoustic representation of the speech signal, here dft for the waveform (time-amplitude representation) and praatFms for the formant measures of vowels (see below).
Levels in an eumR database stand for level of interlinked linguistic information. bundle is the entire audio file, ORT stands for the orthographical representation of the audio file segmented in its single words. MAU is the segmentation of all phonetic segment (=sounds) of all ORT segments in bundle.
The hierarchical structure of these levels is expressed in the link definitions as ONE-TO-MANY.
Database queries
An emuR database can be queried with a powerful query engine. The first example is a simple query for one word, Aarbecht.
sl = query(db, query = "[ ORT == 'Aarbecht']")
sl
The result is a segment list (sl), containing various information about the found item (time, level, name, database info). The result of the query can also be displayed in the EMU Speech Database Management System.
serve(db, seglist = sl)
The GUI will open in the Viewer pane of RStudio or you can open it in a browser (Chrome preferred).

Here we can also display the hiearchical structure for this database item, which is accessed during queries. bundle is the top-level, representing the entire audio file.
The ORT level contains the nodes for the individual words in the bundle, here the two words Aarbecht and Aarbechten. The dependend level then is MAU (=Munich Automatic Unit) representing the single sounds of the words in ORT.

Two aspects render emuR query system extremly powerful: the use of regular expressions (including negation and other extensions) and the combinated query on different levels of the database.
Let’s try more complex queries:
- regular expression, operator
=~, words beginning with Aarbecht...
sl = query(db, query = "[ ORT =~ 'Aarbecht.*']")
sl
Select the vowel [aː] in all words beginning with Aarbecht… Note that in the segment list the label now has changed to the vowel and the respective start-end information is now only for this sound [aː].
sl = query(db, query = "[ ORT =~ 'Aarbecht.*' ^ #MAU=='aː']")
sl
NA
- query a sequence of sounds, e.g.
e followed by k (Méck), by using the sequence operator ->.
sl = query(db, query = "[ MAU == e -> MAU == k ]")
# print only the first 100 rows
sl[1:100, ]
NA
- in the sequence
e->k, query only the vowel. Use the result modifier #.
sl = query(db, query = "[ #MAU == e -> MAU == k ]")
NA
# query all phoneme items that occur
# at the start of a syllable
sl <- query(db, query = "[End(ORT, MAU) == TRUE & MAU == p ]")
sl
# retrieve all word that contain five segments
sl <- query(db, "[Num(ORT, MAU) == 5]")
sl
- using
requery the results from a previous query can be further specified
########################
# requery_seq()
# query all "n" phonetic items
sl_n = query(db, "MAU == m")
# sequential requery (left shift result by 1 (== offset of -1))
# and hence retrieve all phonetic items directly preceeding
# all "m" phonetic items
sl_req_n <- requery_seq(db,
seglist = sl_n,
offset = -1)
sl_req_n
- groups auf sounds can be grouped together to
label groups, e.g. all long monophthongs ("iː", "uː", "aː", "oː", "ɔː", "ɛː", "eː") or all short monophthongs ("i", "u", "ɑ", "o", "æ", "e", "ə", "ɐ").
# query all "m" phonetic items
sl = query(db, "MAU == longMonophthongs")
sl
With the query the user can compile the data frame from the database which then forms the subset for the phonetic analysis. We can select e.g. all instances of certain (or all) vowels, specifying the context before or after etc. etc.
Of course, querying for individual segments in the audio file like words or sounds is possible only, if this information has been added to the database before.
Signal processing in R
The first task is to extract the time-amplitude waveform representation (oscillogram) for certain single sounds, e.g. some long aː.
# query all "aː" phonetic items
#
sl = query(db, "MAU == aː")
# instead of all 7,000 vowels take only the first 6
sl = sl[1:6, ]
# get "fm" track data for these segments
# (verbose = F is only set to avoid additional output)
a_vowels = get_trackdata(emuDBhandle = db,
seglist = sl,
ssffTrackName = "fm",
verbose = FALSE)
Error in get_trackdata(emuDBhandle = db, seglist = sl, ssffTrackName = "fm", :
The emuDB object lod does not have any ssffTrackDefinitions called fm
The data for the oscillogram is stored in the SSF track dft, which is extracted by get_trackdata. The result is another R dataframe.
# get "dft" track data for these segments
a_vowels = get_trackdata(emuDBhandle = db,
seglist = sl,
ssffTrackName = "MEDIAFILE_SAMPLES",
verbose = TRUE)
The emusegs/emuRsegs object passed in refers to bundles with in-homogeneous sampling rates in their audio files! Here is a list of all refered to bundles incl. their sampling rate:
INFO: parsing 6 wav segments/events
|
| | 0%
|
|=========== | 17%
|
|====================== | 33%
|
|================================= | 50%
|
|============================================ | 67%
|
|======================================================= | 83%
|
|==================================================================| 100%
The oscillograms for these 6 vowel instances can then be visualised with ggplot2.
# plot oscillogram
ggplot(data = a_vowels) +
aes(y = T1, x = times_rel) +
geom_line() +
facet_wrap(~sl_rowIdx + labels)

# query all words beginning with 'Fluch'
#
sl = query(db, query = "[ ORT =~ 'Fluch.*']")
# get "f0" track data for these segments, in this case calculated on the fly
fluch_words = get_trackdata(emuDBhandle = db,
seglist = sl,
# using emuR's Michel Scheffers’ Modified Harmonic
# Sieve algorithm
onTheFlyFunctionName = "mhsF0",
verbose = TRUE)
The emusegs/emuRsegs object passed in refers to bundles with in-homogeneous sampling rates in their audio files! Here is a list of all refered to bundles incl. their sampling rate:
INFO: applying mhsF0 to 12 segments/events
|
| | 0%
|
|====== | 8%
|
|=========== | 17%
|
|================ | 25%
|
|====================== | 33%
|
|============================ | 42%
|
|================================= | 50%
|
|====================================== | 58%
|
|============================================ | 67%
|
|================================================== | 75%
|
|======================================================= | 83%
|
|============================================================ | 92%
|
|==================================================================| 100%
In the corresponding graphs then the track for the fundamental frequency (f0) for some isolated words will be drawn.
# plot f0 tracks
ggplot(data = fluch_words) +
# define the df columns for the x and y values
aes(y = T1, x = times_rel) +
# line chart
geom_line() +
# sl_rowIdx groups all rows in a dataframe belonging to the same segment
# labels contains the label of the segment
facet_wrap(~ sl_rowIdx + labels)

If needed, you can check the content of your segment list with the EMU WebApp running serve(seglist = sl).
Calculate duration for vowel categories
The study of duration of speech segments is a standard task in acoustic phonetics. Let’s see how to solve this in R. Thanks to the label groups which are available in the database, we have easy access to e.g. all shortMonophthongs and all longMonophthongs.
# query all "m" phonetic items
longMonophthongs = query(db, "MAU == longMonophthongs")
shortMonophthongs = query(db, "MAU == shortMonophthongs")
# print the count
length(longMonophthongs)
[1] 16
length(shortMonophthongs)
[1] 16
The data frame contains per segment a start and end column, which allows for simple calculation of the segment duration.
# calculate durations
duration_long = longMonophthongs$end - longMonophthongs$start
duration_short = shortMonophthongs$end - shortMonophthongs$start
# calculate mean and standard deviation
paste("The mean duration for long monophthongs in the databse is", round(mean(duration_long)), "ms. The standard deviation is:", sd(duration_long), ".")
[1] "The mean duration for long monophthongs in the databse is 153 ms. The standard deviation is: 62.9196191781937 ."
paste("The mean duration for short monophthongs in the databse is", round(mean(duration_short)), "ms. The standard deviation is:", sd(duration_short), ".")
[1] "The mean duration for short monophthongs in the databse is 106 ms. The standard deviation is: 51.4225215439996 ."
Instead of having these broad categories long and short we can break them down to the individual vowels. The dataframes just created contain the individual vowels already in the labels column and can thus be easily utilised.
head(longMonophthongs)
First, we create a aggregated datafrom with all vowel segments. This aggregated dataframe now contains 127,065 rows (= long/short vowels).
# add new columns for length, either long or short
longMonophthongs$length <- "long"
shortMonophthongs$length <- "short"
# then merge the two data frame into a single one
all_vowels <- rbind(longMonophthongs, shortMonophthongs)
# add a new colum for segment duration
all_vowels$duration <- round(all_vowels$end - all_vowels$start)
Using the powerful library dplyr, we can now calculate the means for all vowels (= labels).
duration_vowels <- all_vowels %>%
group_by(labels, length) %>%
summarise(mean = mean(duration), sd = sd(duration))
duration_vowels
Finally, visualise the means in a bar chart
ggplot(data = duration_vowels, aes(x= labels, y= mean, fill = length)) +
geom_bar(stat="identity", position=position_dodge()) +
geom_errorbar(aes(ymin=mean-sd, ymax=mean+sd), width=.2,
position=position_dodge(.9)) +
theme_minimal()

From here on the duration values can be utilised in further statistical analyses.
Vowels formants and visualisations with ggplot2
Formant frequencies are the acoustic representation of the vowel’s quality. Of these, the first two formants are crucial for the vowels identity, i.e. F1 and F2. The values for the formants are already calculated in the database and will now be extracted for all the vowels our dataframe all_vowels.
As it takes long to do this, the dataframe has been precompiled and saved to file …
saveRDS(td_vowels, file ="td_vowels.RDS")
… to be simply loaded.
td_vowels <- readRDS("td_vowels.RDS")
Formants are calculated for every sampling point of the audio file. Thus, the dataframe td_vowels now contains some 2,3 million rows/values.
For illustration purposes, the formants for the long vowels in three example words are selected into a dataframe (maachen, Viz, Kuuscht).
formant_examples <- td_vowels %>%
filter(sl_rowIdx == "722" | sl_rowIdx == "21702" | sl_rowIdx == "11191")
These three vowels then are visualised as line charts.
ggplot(data=formant_examples) +
geom_smooth(aes(x = times_norm, y = T1, col = labels, group = labels)) +
geom_smooth(aes(x = times_norm, y = T2, col = labels, group = labels)) +
labs(x = "Duration (normalized)", y = "F1 + F2 (Hz)") +
ggtitle("Example formants F1, F2 the three corner vowels") +
facet_wrap(~ labels)

vowel_midpoints = td_vowels %>%
filter(times_norm == 0.4)
Calculate the Pillai distance
Vowel explorer
Try it here.
knitr::include_app("https://petergill.shinyapps.io/shinyplay/")
LS0tCnN1YnRpdGxlOiAiTGVjdHVyZSBmb3IgY2xhc3M6IERhdGEgc2NpZW5jZSBpbiB0aGUgSHVtYW5pdGllcyIKdGl0bGU6ICJCaWcgZGF0YSBpbiB0aGUgYWNvdXN0aWMgcGhvbmV0aWMgYW5hbHlzaXMiCgojIG5lZWRlZCBmb3IgZ2l0aHViCmtuaXQ6IChmdW5jdGlvbihpbnB1dF9maWxlLCBlbmNvZGluZykgewogIG91dF9kaXIgPC0gJ2RvY3MnOwogIHJtYXJrZG93bjo6cmVuZGVyKGlucHV0X2ZpbGUsCiBlbmNvZGluZz1lbmNvZGluZywKIG91dHB1dF9maWxlPWZpbGUucGF0aChkaXJuYW1lKGlucHV0X2ZpbGUpLCBvdXRfZGlyLCAnaW5kZXguaHRtbCcpKX0pCiAKYXV0aG9yOiAiUGV0ZXIgR2lsbGVzIgp1cmw6ICJodHRwczovL3R3aXR0ZXIuY29tL1BldGVyR2lsbGVzIgoKZGF0ZTogIjI5LiBBcHJpbCAyMDIwLCAxNGgwMCAtIDE2aDAwLCBVbml2ZXJzaXR5IG9mIEx1eGVtYm91cmciCm91dHB1dDoKICAjdHVmdGU6OnR1ZnRlX2h0bWw6CiAgI3R1ZnRlOjp0dWZ0ZV9oYW5kb3V0OgogICNwZGZfZG9jdW1lbnQ6CiAgIyAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4CiAgIyAgdG9jOiB0cnVlCiAgIyAgdG9jX2RlcHRoOiAyCiAgIyAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCi0tLQoKVGhpcyB0dXRvcmlhbCB3aWxsIGRlbW9uc3RyYXRlIGhvdyB0byBhbmFseXNlIGF1ZGlvIGRhdGEgZm9yIGFjb3VzdGljIHBob25ldGljIHN0dWRpZXMgaW4gUi4gSXQgaXMgbWFpbmx5IGludGVuZGVkIHRvIGRlbW9uc3RyYXRlZCBwb3NzaWJsZSB3b3JrZmxvd3MuIFRoZSB0b3BpY3MgY292ZXJlZCBhcmU6CgoqIHBob25ldGljIGRhdGFiYXNlcywgdGhlIGNhc2Ugb2YgYGVtdVJgIGFuZCBgRU1VLVNETVNgLCB0aGUgRU1VIFNwZWVjaCBEYXRhYmFzZSBNYW5hZ2VtZW50IFN5c3RlbQoqIFNhbXBsZSBkYXRhOiB0aGUgTE9EIGRhdGFiYXNlCiogUXVlcmllcyBhbmQgcmVxdWVyaWVzCiogSW5zY3BlY3QgdGhlIGRhdGFiYXNlOiBgc2VydmUoKWAKKiBDYWxjdWxhdGUgZHVyYXRpb24gZm9yIHZvd2VsIGNhdGVnb3JpZXMKKiBWb3dlbHMgZm9ybWFudHMgYW5kIHZpc3VhbGlzYXRpb25zIHdpdGggYGdncGxvdDJgCiogVm93ZWwgZXhwbG9yZXIKKiBDYWxjdWxhdGluZyB0aGUgUGlsbGFpIGRpc3RhbmNlCgojIFByZWFtYmxlIHstfQoKVGhlIGdlbmVyYXRlZCBIVE1MIHBhZ2Ugb2YgdGhpcyB0dXRvcmlhbCBpcyBhdmFpbGFibGUgW2hlcmVdKGh0dHBzOi8vcGV0ZXJnaWxsZXMuZ2l0aHViLmlvL0RhdGFfU2NpZW5jZV9IdW1hbml0aWVzL2luZGV4Lm5iLmh0bWwpLgoKVGhpcyB0dXRvcmlhbCBpcyBvcmdhbmlzZWQgYXMgIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBub3RlYm9vay4gVG8gZXhlY3V0ZSB0aGUgY29kZSB3aXRoaW4gdGhpcyBub3RlYm9vayAoZmlsZW5hbWUgYExlY3R1cmUuUm1kYCkgaXQgaGFzIHRvIGJlIG9wZW5kIGluIFJTdHVkaW8uIFdoZW4gZXhlY3V0aW5nIGNvZGUsIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiBJbiBvcmRlciB0byBkbyBzbywgeW91IG5lZWQgdG8gaGF2ZSBSIGFuZCBSU3R1ZGlvIGluc3RhbGxlZC4gV2hlbiB0aGlzIFIgbm90ZWJvb2sgaXMgbG9hZGVkIGludG8gUlN0dWRpbywgeW91IGNhbiBleGNlY3V0ZSBjaHVua3MgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKiwgYWxsb3dpbmcgeW91IHRvIGV4cGVyaW1lbnQgd2l0aCB0aGUgY29kZS4gVGhpcyBoYW5kb3V0IGNvbnRhaW5zIGFsbCBvdXRwdXQgb2YgdGhlIGNvZGUgKHRhYmxlcywgdmlzdWFsaXNhdGlvbnMgZXRjLikuIFRoZSBlYXNpZXN0IHdheSB0byB3b3JrIHdpdGggdGhpcyBSIGNvZGUgaXMgdG8gY2xvbmUgdGhlIGVudGlyZSBwcm9qZWN0IGZyb20gdGhpcyBHaXRIdWIgcmVwb3NpdG9yeS4KClRoZSBmb2xsb3dpbmcgbGlicmFyaWVzIHdpbGwgYmUgbmVlZGVkIGFuZCBoYXZlIHRvIGJlIGluc3RhbGxlZC4KYGBge3IgTGlicmFyaWVzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ2lyYXBoKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkoZW11UikKbGlicmFyeSh0b29scykKbGlicmFyeShyaW8pCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShnZ2lyYXBoKQpsaWJyYXJ5KGh0bWx0b29scykKbGlicmFyeShzaGlueSkKbGlicmFyeShqb2V5cikKbGlicmFyeShrbml0cikKYGBgCgoKIyBUaGUgTE9EIGRhdGFiYXNlCgpUaGUgZGF0YWJhc2UgY29udGFpbnMgdGhlIGF1ZGlvIHJlY29yZGluZ3MgZnJvbSB0aGUgYEzDq3R6ZWJ1ZXJnZXIgT25saW5lIERpY3Rpb25uYWlyZWAgKGF2YWlsYWJsZSBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL3NwZWxsY2hlY2tlci1sdSksIHNwb2tlbiBieSBvbmUgZmVtYWxlIHNwZWFrZXIuIFRoZSBhdWRpbyBmaWxlcyBoYXZlIGJlZW4gYXV0b21hdGljYWxseSBzZWdtZW50ZWQgd2l0aCB0aGUgW01BVVMgdG9vbHNdKGh0dHBzOi8vY2xhcmluLnBob25ldGlrLnVuaS1tdWVuY2hlbi5kZS9CQVNXZWJTZXJ2aWNlcy9pbnRlcmZhY2UpLiBXZSB0aHVzIGhhdmUgYSBkYXRhYmFzZSBjb25pc3Rpbmcgb2YgdGV4dHVhbCBkYXRhLCBiYXNpY2FsbHkgd29yZHMsIGFuZCB0aGUgY29ycmVzcG9uZGluZyBhdWRpbyBkYXRhLiBUaGUgYXVkaW8gZGF0YSBpcyBzZWdtZW50ZWQgaW50byB3b3JkcyBhbmQgcGhvbmV0aWMgc2VnbWVudHMgKHNvdW5kcykuCgpUaGlzIGRhdGFiYXNlIGhhcyBiZWVuIGNyZWF0ZWQgYmVmb3JlaGFuZC4gSW5mb3MgaG93IHRvIGNyZWF0ZSBzdWNoIGEgZGF0YWJhc2UgaXMgZXhwbGFpbmVkIGluIHRoZSBbRU1VLVNETVMgbWFudWFsXShodHRwczovL2lwcy1sbXUuZ2l0aHViLmlvL1RoZS1FTVUtU0RNUy1NYW51YWwvKS4KCldoaWxlIHdlIHdpbGwgYmUgd29ya2luZyB3aXRoIHRoZSBmdWxsIGRhdGFiYXNlICgyNi4wMDAgcmVjb3JkaW5ncyksIGEgc21hbGwgZGVtbyBkYXRhYmFzZSBgbG9kX2VtdURCYCBoYXMgYmVlbiBjcmVhdGVkIHdpdGggNTAwIHJlY29yZGluZ3Mgd2hpY2ggY2FuIGJlIHVzZWQgZm9yIGluZGl2aWR1YWwgdGVzdGluZy4KCldlIHN0YXJ0IHdpdGggbG9hZGluZyB0aGUgZGF0YWJhc2UgYW5kIGdpdmUgYW4gb3ZlcnZpZXcgb2Ygc3RydWN0dXJlIGFuZCBjb250ZW50LgpgYGB7cn0KIyBsb2FkIGRhdGFiYXNlCmRiID0gbG9hZF9lbXVEQigiL1VzZXJzL3BldGVyLmdpbGxlcy9Eb2N1bWVudHMvX0RhdGVuL0xPRC1lbXVEQi9sb2RfZW11REIiKQojZGIgPSBsb2FkX2VtdURCKCJsb2RfZW11REIiKQoKIyBkaXNwbGF5IHRoZSBvdmVydmlldyBvZiB0aGUgc3RydWN0dXJlIGFuZCBjb250ZW50CnN1bW1hcnkoZGIpCgpgYGAKClRyYWNrcyBpbiBgZW11UmAgYXJlIGFjb3VzdGljIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBzcGVlY2ggc2lnbmFsLCBoZXJlIGBkZnRgIGZvciB0aGUgd2F2ZWZvcm0gKHRpbWUtYW1wbGl0dWRlIHJlcHJlc2VudGF0aW9uKSBhbmQgYHByYWF0Rm1zYCBmb3IgdGhlIGZvcm1hbnQgbWVhc3VyZXMgb2Ygdm93ZWxzIChzZWUgYmVsb3cpLiAKCkxldmVscyBpbiBhbiBgZXVtUmAgZGF0YWJhc2Ugc3RhbmQgZm9yIGxldmVsIG9mIGludGVybGlua2VkIGxpbmd1aXN0aWMgaW5mb3JtYXRpb24uIGBidW5kbGVgIGlzIHRoZSBlbnRpcmUgYXVkaW8gZmlsZSwgYE9SVGAgc3RhbmRzIGZvciB0aGUgb3J0aG9ncmFwaGljYWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGF1ZGlvIGZpbGUgc2VnbWVudGVkIGluIGl0cyBzaW5nbGUgd29yZHMuIGBNQVVgIGlzIHRoZSBzZWdtZW50YXRpb24gb2YgYWxsIHBob25ldGljIHNlZ21lbnQgKD1zb3VuZHMpIG9mIGFsbCBgT1JUYCBzZWdtZW50cyBpbiBgYnVuZGxlYC4KClRoZSBoaWVyYXJjaGljYWwgc3RydWN0dXJlIG9mIHRoZXNlIGxldmVscyBpcyBleHByZXNzZWQgaW4gdGhlIGBsaW5rIGRlZmluaXRpb25zYCBhcyBgT05FLVRPLU1BTllgLgoKIyBEYXRhYmFzZSBxdWVyaWVzCgpBbiBlbXVSIGRhdGFiYXNlIGNhbiBiZSBxdWVyaWVkIHdpdGggYSBwb3dlcmZ1bCBxdWVyeSBlbmdpbmUuIFRoZSBmaXJzdCBleGFtcGxlIGlzIGEgc2ltcGxlIHF1ZXJ5IGZvciBvbmUgd29yZCwgYEFhcmJlY2h0YC4KCmBgYHtyfQpzbCA9IHF1ZXJ5KGRiLCBxdWVyeSA9ICJbIE9SVCA9PSAnQWFyYmVjaHQnXSIpCnNsCmBgYAoKVGhlIHJlc3VsdCBpcyBhIGBzZWdtZW50IGxpc3RgIChgc2xgKSwgY29udGFpbmluZyB2YXJpb3VzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBmb3VuZCBpdGVtICh0aW1lLCBsZXZlbCwgbmFtZSwgZGF0YWJhc2UgaW5mbykuIFRoZSByZXN1bHQgb2YgdGhlIHF1ZXJ5IGNhbiBhbHNvIGJlIGRpc3BsYXllZCBpbiB0aGUgRU1VIFNwZWVjaCBEYXRhYmFzZSBNYW5hZ2VtZW50IFN5c3RlbS4gCgpgc2VydmUoZGIsIHNlZ2xpc3QgPSBzbClgCgpUaGUgR1VJIHdpbGwgb3BlbiBpbiB0aGUgYFZpZXdlcmAgcGFuZSBvZiBSU3R1ZGlvIG9yIHlvdSBjYW4gb3BlbiBpdCBpbiBhIGJyb3dzZXIgKENocm9tZSBwcmVmZXJyZWQpLgoKYGBge3IgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocmVwKCJlbXUtc2Rtcy5wbmciKSkKYGBgCgpIZXJlIHdlIGNhbiBhbHNvIGRpc3BsYXkgdGhlIGhpZWFyY2hpY2FsIHN0cnVjdHVyZSBmb3IgdGhpcyBkYXRhYmFzZSBpdGVtLCB3aGljaCBpcyBhY2Nlc3NlZCBkdXJpbmcgcXVlcmllcy4gYGJ1bmRsZWAgaXMgdGhlIHRvcC1sZXZlbCwgcmVwcmVzZW50aW5nIHRoZSBlbnRpcmUgYXVkaW8gZmlsZS4gCgpUaGUgYE9SVGAgbGV2ZWwgY29udGFpbnMgdGhlIG5vZGVzIGZvciB0aGUgaW5kaXZpZHVhbCB3b3JkcyBpbiB0aGUgYGJ1bmRsZWAsIGhlcmUgdGhlIHR3byB3b3JkcyBgQWFyYmVjaHRgIGFuZCBgQWFyYmVjaHRlbmAuIFRoZSBkZXBlbmRlbmQgbGV2ZWwgdGhlbiBpcyBgTUFVYCAoPWBNdW5pY2ggQXV0b21hdGljIFVuaXRgKSByZXByZXNlbnRpbmcgdGhlIHNpbmdsZSBzb3VuZHMgb2YgdGhlIHdvcmRzIGluIGBPUlRgLiAKCmBgYHtyIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHJlcCgiaGllcmFyY2h5LnBuZyIpKQpgYGAKClR3byBhc3BlY3RzIHJlbmRlciBlbXVSIHF1ZXJ5IHN5c3RlbSBleHRyZW1seSBwb3dlcmZ1bDogdGhlIHVzZSBvZiByZWd1bGFyIGV4cHJlc3Npb25zIChpbmNsdWRpbmcgbmVnYXRpb24gYW5kIG90aGVyIGV4dGVuc2lvbnMpIGFuZCB0aGUgY29tYmluYXRlZCBxdWVyeSBvbiBkaWZmZXJlbnQgbGV2ZWxzIG9mIHRoZSBkYXRhYmFzZS4KCkxldCdzIHRyeSBtb3JlIGNvbXBsZXggcXVlcmllczoKCi0gcmVndWxhciBleHByZXNzaW9uLCBvcGVyYXRvciBgPX5gLCB3b3JkcyBiZWdpbm5pbmcgd2l0aCBgQWFyYmVjaHQuLi5gCmBgYHtyfQpzbCA9IHF1ZXJ5KGRiLCBxdWVyeSA9ICJbIE9SVCA9fiAnQWFyYmVjaHQuKiddIikKc2wKYGBgCgpTZWxlY3QgdGhlIHZvd2VsIFthy5BdIGluIGFsbCB3b3JkcyBiZWdpbm5pbmcgd2l0aCBgQWFyYmVjaHRgLi4uIE5vdGUgdGhhdCBpbiB0aGUgc2VnbWVudCBsaXN0IHRoZSBsYWJlbCBub3cgaGFzIGNoYW5nZWQgdG8gdGhlIHZvd2VsIGFuZCB0aGUgcmVzcGVjdGl2ZSBzdGFydC1lbmQgaW5mb3JtYXRpb24gaXMgbm93IG9ubHkgZm9yIHRoaXMgc291bmQgW2HLkF0uCgpgYGB7cn0Kc2wgPSBxdWVyeShkYiwgcXVlcnkgPSAiWyBPUlQgPX4gJ0FhcmJlY2h0LionIF4gI01BVT09J2HLkCddIikKc2wKCmBgYAoKLSBxdWVyeSBhIHNlcXVlbmNlIG9mIHNvdW5kcywgZS5nLiBgZWAgZm9sbG93ZWQgYnkgYGtgIChgTcOpY2tgKSwgYnkgdXNpbmcgdGhlIHNlcXVlbmNlIG9wZXJhdG9yIGAtPmAuCgpgYGB7cn0Kc2wgPSBxdWVyeShkYiwgcXVlcnkgPSAiWyBNQVUgPT0gZSAtPiBNQVUgPT0gayBdIikKIyBwcmludCBvbmx5IHRoZSBmaXJzdCAxMDAgcm93cwpzbFsxOjEwMCwgXQoKYGBgCgotIGluIHRoZSBzZXF1ZW5jZSBgZS0+a2AsIHF1ZXJ5IG9ubHkgdGhlIHZvd2VsLiBVc2UgdGhlIHJlc3VsdCBtb2RpZmllciBgI2AuCgpgYGB7cn0Kc2wgPSBxdWVyeShkYiwgcXVlcnkgPSAiWyAjTUFVID09IGUgLT4gTUFVID09IGsgXSIpCiMgcHJpbnQgb25seSB0aGUgZmlyc3QgMTAwIHJvd3MKc2xbMToxMDAsIF0KYGBgCgpgYGB7cn0KIyBxdWVyeSBhbGwgc291bmQgaXRlbXMgdGhhdCBvY2N1cgojIGF0IHRoZSBlbmQgb2YgYSB3b3JkIGFuZCBhcmUgYHBgCnNsIDwtIHF1ZXJ5KGRiLCBxdWVyeSA9ICJbRW5kKE9SVCwgTUFVKSA9PSBUUlVFICYgTUFVID09IHAgXSIpCnNsCmBgYAoKYGBge3J9CiMgcmV0cmlldmUgYWxsIHdvcmQgdGhhdCBjb250YWluIGZpdmUgc2VnbWVudHMKc2wgPC0gcXVlcnkoZGIsICJbTnVtKE9SVCwgTUFVKSA9PSA1XSIpCnNsCmBgYAoKKiB1c2luZyBgcmVxdWVyeWAgdGhlIHJlc3VsdHMgZnJvbSBhIHByZXZpb3VzIHF1ZXJ5IGNhbiBiZSBmdXJ0aGVyIHNwZWNpZmllZAoKYGBge3J9CiMgcmVxdWVyeV9zZXEoKQoKIyBxdWVyeSBhbGwgIm0iIHBob25ldGljIGl0ZW1zCnNsX20gPSBxdWVyeShkYiwgIk1BVSA9PSBtIikKCiMgc2VxdWVudGlhbCByZXF1ZXJ5IChsZWZ0IHNoaWZ0IHJlc3VsdCBieSAxICg9PSBvZmZzZXQgb2YgLTEpKQojIGFuZCBoZW5jZSByZXRyaWV2ZSBhbGwgcGhvbmV0aWMgaXRlbXMgZGlyZWN0bHkgcHJlY2VlZGluZwojIGFsbCAibSIgcGhvbmV0aWMgaXRlbXMKc2xfcmVxX24gPC0gcmVxdWVyeV9zZXEoZGIsIAogICAgICAgICAgICBzZWdsaXN0ID0gc2xfbSwgCiAgICAgICAgICAgIG9mZnNldCA9IC0xKQpzbF9yZXFfbgpgYGAKCiogZ3JvdXBzIGF1ZiBzb3VuZHMgY2FuIGJlIGdyb3VwZWQgdG9nZXRoZXIgdG8gYGxhYmVsIGdyb3Vwc2AsIGUuZy4gYWxsIGxvbmcgbW9ub3BodGhvbmdzIChgImnLkCIsICJ1y5AiLCAiYcuQIiwgIm/LkCIsICLJlMuQIiwgIsmby5AiLCAiZcuQImApIG9yIGFsbCBzaG9ydCBtb25vcGh0aG9uZ3MgKGAiaSIsICJ1IiwgIsmRIiwgIm8iLCAiw6YiLCAiZSIsICLJmSIsICLJkCJgKS4KCmBgYHtyfQojIHF1ZXJ5IGFsbCAibSIgcGhvbmV0aWMgaXRlbXMKc2wgPSBxdWVyeShkYiwgIk1BVSA9PSBsb25nTW9ub3BodGhvbmdzIikKc2wKYGBgCgpXaXRoIHRoZSBxdWVyeSB0aGUgdXNlciBjYW4gY29tcGlsZSB0aGUgZGF0YSBmcmFtZSBmcm9tIHRoZSBkYXRhYmFzZSB3aGljaCB0aGVuIGZvcm1zIHRoZSBzdWJzZXQgZm9yIHRoZSBwaG9uZXRpYyBhbmFseXNpcy4gV2UgY2FuIHNlbGVjdCBlLmcuIGFsbCBpbnN0YW5jZXMgb2YgY2VydGFpbiAob3IgYWxsKSB2b3dlbHMsIHNwZWNpZnlpbmcgdGhlIGNvbnRleHQgYmVmb3JlIG9yIGFmdGVyIGV0Yy4gZXRjLgoKT2YgY291cnNlLCBxdWVyeWluZyBmb3IgaW5kaXZpZHVhbCBzZWdtZW50cyBpbiB0aGUgYXVkaW8gZmlsZSBsaWtlIHdvcmRzIG9yIHNvdW5kcyBpcyBwb3NzaWJsZSBvbmx5LCBpZiB0aGlzIGluZm9ybWF0aW9uIGhhcyBiZWVuIGFkZGVkIHRvIHRoZSBkYXRhYmFzZSBiZWZvcmUuCgoKIyBTaWduYWwgcHJvY2Vzc2luZyBpbiBSCgpUaGUgZmlyc3QgdGFzayBpcyB0byBleHRyYWN0IHRoZSB0aW1lLWFtcGxpdHVkZSB3YXZlZm9ybSByZXByZXNlbnRhdGlvbiAob3NjaWxsb2dyYW0pIGZvciBjZXJ0YWluIHNpbmdsZSBzb3VuZHMsIGUuZy4gc29tZSBsb25nIGBhy5BgLgoKYGBge3J9CiMgcXVlcnkgYWxsICJhy5AiIHBob25ldGljIGl0ZW1zCiMgCnNsID0gcXVlcnkoZGIsICJNQVUgPT0gYcuQIikKIyBpbnN0ZWFkIG9mIGFsbCA3LDAwMCB2b3dlbHMgdGFrZSBvbmx5IDYKc2wgPSBzbFsxMDA6MTA1LCBdCgpgYGAKClRoZSBkYXRhIGZvciB0aGUgb3NjaWxsb2dyYW0gaXMgc3RvcmVkIGluIHRoZSBTU0YgdHJhY2sgYGRmdGAsIHdoaWNoIGlzIGV4dHJhY3RlZCBieSBgZ2V0X3RyYWNrZGF0YWAuIFRoZSByZXN1bHQgaXMgYW5vdGhlciBSIGRhdGFmcmFtZS4KCmBgYHtyfQojIGdldCAiZGZ0IiB0cmFjayBkYXRhIGZvciB0aGVzZSBzZWdtZW50cwphX3Zvd2VscyA9IGdldF90cmFja2RhdGEoZW11REJoYW5kbGUgPSBkYiwKICAgICAgICAgICAgICAgICAgICAgICAgIHNlZ2xpc3QgPSBzbCwKICAgICAgICAgICAgICAgICAgICAgICAgIHNzZmZUcmFja05hbWUgPSAiTUVESUFGSUxFX1NBTVBMRVMiLAogICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpCmBgYAoKVGhlIG9zY2lsbG9ncmFtcyBmb3IgdGhlc2UgNiB2b3dlbCBpbnN0YW5jZXMgY2FuIHRoZW4gYmUgdmlzdWFsaXNlZCB3aXRoIGBnZ3Bsb3QyYC4KCmBgYHtyfQojIHBsb3Qgb3NjaWxsb2dyYW0KZ2dwbG90KGRhdGEgPSBhX3Zvd2VscykgKyAKICAjIGRlZmluZSB0aGUgZGYgY29sdW1ucyBmb3IgdGhlIHggYW5kIHkgdmFsdWVzCiAgYWVzKHkgPSBUMSwgeCA9IHRpbWVzX3JlbCkgKyAKICAjIGxpbmUgY2hhcnQKICBnZW9tX2xpbmUoKSArIAogICMgc2xfcm93SWR4IGdyb3VwcyBhbGwgcm93cyBpbiBhIGRhdGFmcmFtZSBiZWxvbmdpbmcgdG8gdGhlIHNhbWUgc2VnbWVudAogICMgbGFiZWxzIGNvbnRhaW5zIHRoZSBsYWJlbCBvZiB0aGUgc2VnbWVudAogIGZhY2V0X3dyYXAofiBzbF9yb3dJZHggKyBsYWJlbHMpCmBgYAoKYGBge3J9CiMgcXVlcnkgYWxsIHdvcmRzIGJlZ2lubmluZyB3aXRoICdGbHVjaCcKIyAKc2wgPSBxdWVyeShkYiwgcXVlcnkgPSAiWyBPUlQgPX4gJ0ZsdWNoLionXSIpCgojIGdldCAiZjAiIHRyYWNrIGRhdGEgZm9yIHRoZXNlIHNlZ21lbnRzLCBpbiB0aGlzIGNhc2UgY2FsY3VsYXRlZCBvbiB0aGUgZmx5CmZsdWNoX3dvcmRzID0gZ2V0X3RyYWNrZGF0YShlbXVEQmhhbmRsZSA9IGRiLAogICAgICAgICAgICAgICAgICAgICAgICAgc2VnbGlzdCA9IHNsLAogICAgICAgICAgICAgICAgICAgICAgICAgIyB1c2luZyBlbXVSJ3MgTWljaGVsIFNjaGVmZmVyc+KAmSBNb2RpZmllZCBIYXJtb25pYwogICAgICAgICAgICAgICAgICAgICAgICAgIyBTaWV2ZSBhbGdvcml0aG0gCiAgICAgICAgICAgICAgICAgICAgICAgICBvblRoZUZseUZ1bmN0aW9uTmFtZSA9ICJtaHNGMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJib3NlID0gVFJVRSkKYGBgCgpJbiB0aGUgY29ycmVzcG9uZGluZyBncmFwaHMgdGhlbiB0aGUgdHJhY2sgZm9yIHRoZSBmdW5kYW1lbnRhbCBmcmVxdWVuY3kgKGYwKSBmb3Igc29tZSBpc29sYXRlZCB3b3JkcyB3aWxsIGJlIGRyYXduLgoKYGBge3J9CiMgcGxvdCBmMCB0cmFja3MKZ2dwbG90KGRhdGEgPSBmbHVjaF93b3JkcykgKyAKICAjIGRlZmluZSB0aGUgZGYgY29sdW1ucyBmb3IgdGhlIHggYW5kIHkgdmFsdWVzCiAgIyBUMSBoZXJlIGlzIHRoZSBmMCB2YWx1ZQogIGFlcyh5ID0gVDEsIHggPSB0aW1lc19yZWwpICsgCiAgIyBsaW5lIGNoYXJ0CiAgZ2VvbV9saW5lKCkgKyAKICAjIHNsX3Jvd0lkeCBncm91cHMgYWxsIHJvd3MgaW4gYSBkYXRhZnJhbWUgYmVsb25naW5nIHRvIHRoZSBzYW1lIHNlZ21lbnQKICAjIGxhYmVscyBjb250YWlucyB0aGUgbGFiZWwgb2YgdGhlIHNlZ21lbnQKICBmYWNldF93cmFwKH4gc2xfcm93SWR4ICsgbGFiZWxzKQpgYGAKCklmIG5lZWRlZCwgeW91IGNhbiBjaGVjayB0aGUgY29udGVudCBvZiB5b3VyIHNlZ21lbnQgbGlzdCB3aXRoIHRoZSBFTVUgV2ViQXBwIHJ1bm5pbmcgYHNlcnZlKHNlZ2xpc3QgPSBzbClgLgoKIyBDYWxjdWxhdGUgZHVyYXRpb24gZm9yIHZvd2VsIGNhdGVnb3JpZXMKClRoZSBzdHVkeSBvZiBkdXJhdGlvbiBvZiBzcGVlY2ggc2VnbWVudHMgaXMgYSBzdGFuZGFyZCB0YXNrIGluIGFjb3VzdGljIHBob25ldGljcy4gTGV0J3Mgc2VlIGhvdyB0byBzb2x2ZSB0aGlzIGluIGBSYC4gVGhhbmtzIHRvIHRoZSBgbGFiZWwgZ3JvdXBzYCB3aGljaCBhcmUgYXZhaWxhYmxlIGluIHRoZSBkYXRhYmFzZSwgd2UgaGF2ZSBlYXN5IGFjY2VzcyB0byBlLmcuIGFsbCBgc2hvcnRNb25vcGh0aG9uZ3NgIGFuZCBhbGwgYGxvbmdNb25vcGh0aG9uZ3NgLgoKYGBge3J9CiMgcXVlcnkgYWxsICJtIiBwaG9uZXRpYyBpdGVtcwpsb25nTW9ub3BodGhvbmdzID0gcXVlcnkoZGIsICJNQVUgPT0gbG9uZ01vbm9waHRob25ncyIpCnNob3J0TW9ub3BodGhvbmdzID0gcXVlcnkoZGIsICJNQVUgPT0gc2hvcnRNb25vcGh0aG9uZ3MiKQoKIyBwcmludCB0aGUgY291bnQ7IG51bWJlciBvZiByb3dzCm5yb3cobG9uZ01vbm9waHRob25ncykKbnJvdyhzaG9ydE1vbm9waHRob25ncykKYGBgCgpUaGUgZGF0YSBmcmFtZSBjb250YWlucyBwZXIgc2VnbWVudCBhIHN0YXJ0IGFuZCBlbmQgY29sdW1uLCB3aGljaCBhbGxvd3MgZm9yIHNpbXBsZSBjYWxjdWxhdGlvbiBvZiB0aGUgc2VnbWVudCBkdXJhdGlvbi4KCmBgYHtyfQojIGNhbGN1bGF0ZSBkdXJhdGlvbnMKZHVyYXRpb25fbG9uZyA9IGxvbmdNb25vcGh0aG9uZ3MkZW5kIC0gbG9uZ01vbm9waHRob25ncyRzdGFydApkdXJhdGlvbl9zaG9ydCA9IHNob3J0TW9ub3BodGhvbmdzJGVuZCAtIHNob3J0TW9ub3BodGhvbmdzJHN0YXJ0CgojIGNhbGN1bGF0ZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24KcGFzdGUoIlRoZSBtZWFuIGR1cmF0aW9uIGZvciBsb25nIG1vbm9waHRob25ncyBpbiB0aGUgZGF0YWJzZSBpcyIsIHJvdW5kKG1lYW4oZHVyYXRpb25fbG9uZykpLCAibXMuIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaXM6Iiwgc2QoZHVyYXRpb25fbG9uZyksICIuIikKCnBhc3RlKCJUaGUgbWVhbiBkdXJhdGlvbiBmb3Igc2hvcnQgbW9ub3BodGhvbmdzIGluIHRoZSBkYXRhYnNlIGlzIiwgcm91bmQobWVhbihkdXJhdGlvbl9zaG9ydCkpLCAibXMuIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gaXM6Iiwgc2QoZHVyYXRpb25fc2hvcnQpLCAiLiIpCmBgYAoKSW5zdGVhZCBvZiBoYXZpbmcgdGhlc2UgYnJvYWQgY2F0ZWdvcmllcyBgbG9uZ2AgYW5kIGBzaG9ydGAgd2UgY2FuIGJyZWFrIHRoZW0gZG93biB0byB0aGUgaW5kaXZpZHVhbCB2b3dlbHMuIFRoZSBkYXRhZnJhbWVzIGp1c3QgY3JlYXRlZCBjb250YWluIHRoZSBpbmRpdmlkdWFsIHZvd2VscyBhbHJlYWR5IGluIHRoZSBgbGFiZWxzYCBjb2x1bW4gYW5kIGNhbiB0aHVzIGJlIGVhc2lseSB1dGlsaXNlZC4KCmBgYHtyfQpoZWFkKGxvbmdNb25vcGh0aG9uZ3MpCmBgYAoKRmlyc3QsIHdlIGNyZWF0ZSBhIGFnZ3JlZ2F0ZWQgZGF0YWZyb20gd2l0aCBhbGwgdm93ZWwgc2VnbWVudHMuIFRoaXMgYWdncmVnYXRlZCBkYXRhZnJhbWUgbm93IGNvbnRhaW5zIDEyNywwNjUgcm93cyAoPSBsb25nL3Nob3J0IHZvd2VscykuCgpgYGB7cn0KIyBhZGQgbmV3IGNvbHVtbnMgZm9yIGxlbmd0aCwgZWl0aGVyIGxvbmcgb3Igc2hvcnQKbG9uZ01vbm9waHRob25ncyRsZW5ndGggPC0gImxvbmciCnNob3J0TW9ub3BodGhvbmdzJGxlbmd0aCA8LSAic2hvcnQiCgojIHRoZW4gbWVyZ2UgdGhlIHR3byBkYXRhIGZyYW1lIGludG8gYSBzaW5nbGUgb25lCmFsbF92b3dlbHMgPC0gcmJpbmQobG9uZ01vbm9waHRob25ncywgc2hvcnRNb25vcGh0aG9uZ3MpCgojIGFkZCBhIG5ldyBjb2x1bSBmb3Igc2VnbWVudCBkdXJhdGlvbgphbGxfdm93ZWxzJGR1cmF0aW9uIDwtIHJvdW5kKGFsbF92b3dlbHMkZW5kIC0gYWxsX3Zvd2VscyRzdGFydCkKYGBgCgpVc2luZyB0aGUgcG93ZXJmdWwgbGlicmFyeSBgZHBseXJgLCB3ZSBjYW4gbm93IGNhbGN1bGF0ZSB0aGUgbWVhbnMgZm9yIGFsbCB2b3dlbHMgKD0gYGxhYmVsc2ApLgoKYGBge3J9CmR1cmF0aW9uX3Zvd2VscyA8LSBhbGxfdm93ZWxzICU+JQogIGdyb3VwX2J5KGxhYmVscywgbGVuZ3RoKSAlPiUKICBzdW1tYXJpc2UobWVhbiA9IG1lYW4oZHVyYXRpb24pLCBzZCA9IHNkKGR1cmF0aW9uKSkKCmR1cmF0aW9uX3Zvd2VscwpgYGAKCkZpbmFsbHksIHZpc3VhbGlzZSB0aGUgbWVhbnMgaW4gYSBiYXIgY2hhcnQKCmBgYHtyfQojIGJhc2ljIHNldHRpbmcgZm9yIGdncGxvdCB3aXRoIGRhdGEgYW5kIGFlc3RoZXRpY3MKZ2dwbG90KGRhdGEgPSBkdXJhdGlvbl92b3dlbHMsIGFlcyh4PSBsYWJlbHMsIHk9IG1lYW4sIGZpbGwgPSBsZW5ndGgpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSgpKSArCiAgIyBhZGQgZXJyb3JiYXJzCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbj1tZWFuLXNkLCB5bWF4PW1lYW4rc2QpLCB3aWR0aD0uMiwKICAgICAgICAgICAgICAgICBwb3NpdGlvbj1wb3NpdGlvbl9kb2RnZSguOSkpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpGcm9tIGhlcmUgb24gdGhlIGR1cmF0aW9uIHZhbHVlcyBjYW4gYmUgdXRpbGlzZWQgaW4gZnVydGhlciBzdGF0aXN0aWNhbCBhbmFseXNlcy4KCiMgVm93ZWxzIGZvcm1hbnRzIGFuZCB2aXN1YWxpc2F0aW9ucyB3aXRoIGBnZ3Bsb3QyYAoKRm9ybWFudCBmcmVxdWVuY2llcyBhcmUgdGhlIGFjb3VzdGljIHJlcHJlc2VudGF0aW9uIG9mIHRoZSB2b3dlbCdzIHF1YWxpdHkuIE9mIHRoZXNlLCB0aGUgZmlyc3QgdHdvIGZvcm1hbnRzIGFyZSBjcnVjaWFsIGZvciB0aGUgdm93ZWxzIGlkZW50aXR5LCBpLmUuIEYxIGFuZCBGMi4gVGhlIHZhbHVlcyBmb3IgdGhlIGZvcm1hbnRzIGFyZSBhbHJlYWR5IGNhbGN1bGF0ZWQgaW4gdGhlIGRhdGFiYXNlIGFuZCB3aWxsIG5vdyBiZSBleHRyYWN0ZWQgZm9yIGFsbCB0aGUgdm93ZWxzIG91ciBkYXRhZnJhbWUgYGFsbF92b3dlbHNgLgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KIyBnZXQgZm9ybWFudCB2YWx1ZXMgZm9yIHRob3NlIHNlZ21lbnRzCiMgZWl0aGVyIGZyb20gcHJlLWdlbmVyYXRlZCBQcmFhdC1UcmFjawojdGRfdm93ZWxzID0gZ2V0X3RyYWNrZGF0YShkYiwgYWxsX3Zvd2VscywgCiMgICAgICAgICAgICAgICAgICAgICAgICAgIHNzZmZUcmFja05hbWUgPSAicHJhYXRGbXMiLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0VHlwZSA9ICJ0aWJibGUiKQpgYGAKQXMgaXQgdGFrZXMgbG9uZyB0byBkbyB0aGlzLCB0aGUgZGF0YWZyYW1lIGhhcyBiZWVuIHByZWNvbXBpbGVkIGFuZCBzYXZlZCB0byBmaWxlIC4uLgpgYGB7ciBlY2hvPVRSVUV9CiMgc2F2ZVJEUyh0ZF92b3dlbHMsIGZpbGUgPSJ0ZF92b3dlbHMuUkRTIikKYGBgCgouLi4gdG8gYmUgc2ltcGx5IGxvYWRlZC4KYGBge3J9CnRkX3Zvd2VscyA8LSByZWFkUkRTKCJ0ZF92b3dlbHMuUkRTIikKYGBgCgpGb3JtYW50cyBhcmUgY2FsY3VsYXRlZCBmb3IgZXZlcnkgc2FtcGxpbmcgcG9pbnQgb2YgdGhlIGF1ZGlvIGZpbGUuIFRodXMsIHRoZSBkYXRhZnJhbWUgYHRkX3Zvd2Vsc2Agbm93IGNvbnRhaW5zIHNvbWUgMiwzIG1pbGxpb24gcm93cy92YWx1ZXMuCgpGb3IgaWxsdXN0cmF0aW9uIHB1cnBvc2VzLCB0aGUgZm9ybWFudHMgZm9yIHRoZSBsb25nIHZvd2VscyBpbiB0aHJlZSBleGFtcGxlIHdvcmRzIGFyZSBzZWxlY3RlZCBpbnRvIGEgZGF0YWZyYW1lICgqbWFhY2hlbiwgVml6LCBLdXVzY2h0KikuCgpgYGB7cn0KZm9ybWFudF9leGFtcGxlcyA8LSB0ZF92b3dlbHMgJT4lCiAgZmlsdGVyKHNsX3Jvd0lkeCA9PSAiNzIyIiB8IHNsX3Jvd0lkeCA9PSAiMjE3MDIiIHwgc2xfcm93SWR4ID09ICIxMTE5MSIpCmBgYAoKVGhlc2UgdGhyZWUgdm93ZWxzIHRoZW4gYXJlIHZpc3VhbGlzZWQgYXMgbGluZSBjaGFydHMuCgpgYGB7ciBtZXNzYWdlPUZBTFNFfQpnZ3Bsb3QoZGF0YT1mb3JtYW50X2V4YW1wbGVzKSArCiAgZ2VvbV9zbW9vdGgoYWVzKHggPSB0aW1lc19ub3JtLCB5ID0gVDEsIGNvbCA9IGxhYmVscywgZ3JvdXAgPSBsYWJlbHMpKSArCiAgZ2VvbV9zbW9vdGgoYWVzKHggPSB0aW1lc19ub3JtLCB5ID0gVDIsIGNvbCA9IGxhYmVscywgZ3JvdXAgPSBsYWJlbHMpKSArCiAgbGFicyh4ID0gIkR1cmF0aW9uIChub3JtYWxpemVkKSIsIHkgPSAiRjEgKyBGMiAoSHopIikgKwogIGdndGl0bGUoIkV4YW1wbGUgZm9ybWFudHMgRjEsIEYyIHRoZSB0aHJlZSBjb3JuZXIgdm93ZWxzIikgKwogIGZhY2V0X3dyYXAofiBsYWJlbHMpCmBgYAoKYGBge3J9CnZvd2VsX21pZHBvaW50cyA9IHRkX3Zvd2VscyAlPiUgCiAgZmlsdGVyKHRpbWVzX25vcm0gPT0gMC40KQpgYGAKCgojIENhbGN1bGF0ZSB0aGUgUGlsbGFpIGRpc3RhbmNlCgojIyBmcm9tIGh0dHBzOi8vam9leXN0YW5sZXkuY29tL2Jsb2cvYS10dXRvcmlhbC1pbi1jYWxjdWxhdGluZy12b3dlbC1vdmVybGFwCgpgYGB7ciBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhyZXAoInBpbGxhaV9leGFtcGxlLnBuZyIpKQpgYGAKCiMgVm93ZWwgZXhwbG9yZXIKClRyeSBpdCBbaGVyZV0oaHR0cHM6Ly9wZXRlcmdpbGwuc2hpbnlhcHBzLmlvL3NoaW55cGxheS8pLgoKYGBge3J9Cgprbml0cjo6aW5jbHVkZV9hcHAoImh0dHBzOi8vcGV0ZXJnaWxsLnNoaW55YXBwcy5pby9zaGlueXBsYXkvIikKCmBgYAoKCgo=